 
package cn.demoncat.util.lang;

import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ANT路径通配符工具
 * <pre>
 * ANT语法规则：
 * 	?	= 单个字符串，包含/
 *	*	= 任意字符串，不包含/
 *	**	= 任意字符串，包含/
 *	/*	= 匹配 /xx
 *	/**	= 匹配 /xx/xx/xx...
 * </pre>
 * @author 延晓磊
 */
public final class AntUtil {
 
	/**
	 * ANT通配符：? ** *
	 */
	private static final String WILDCARD = "\\?|\\*\\*|\\*";
	private static final String S1 = ".*";
	private static final String S2 = "[^/]*";
	private static final String S3 = "\\Q";
	private static final String S4 = "\\E";
	private static final String S5 = "\\\\Q";
	private static final String S6 = "\\\\E";
	private static final String S7 = "\\.\\*";
	private static final String S8 = "\\[\\^/]\\*";
	private static final String S9 = "\\.";


	/**
	 * ANT路径匹配
	 * 
	 * @param path		路径
	 * @param antPath 	ANT路径：? 单个字符，包含/；* 多个字符，不包含/；** 多个字符，包含/
	 * 
	 * @return 匹配返回true
	 *
	 * @author 延晓磊
	 */
	public static boolean ant(String path, String antPath){
		path = StringUtils.trimToEmpty(path);
		antPath = StringUtils.trimToEmpty(antPath);
		//相等直接为true
		if(path.equals(antPath)) return true;
		//不相等正则匹配
		return path.matches(toRegex(antPath));
	}
	/**
	 * 判断是否为ANT语法的路径
	 * 
	 * @param path 路径
	 * @return 不包含?和*时返回false
	 *
	 * @author 延晓磊
	 */
	public static boolean isAntPath(String path){
		return path.contains(StringConstant.QUESTION) || path.contains(StringConstant.STAR);
	}
	/**
	 * 转换ANT路径为正则表达式
	 *
	 * @param path 路径
	 * @return 正则表达式
	 * 
	 * @author 延晓磊
	 */
	public static String toRegex(String path) {
		path = StringUtils.trimToEmpty(path);
		//非ANT语法
		if(! isAntPath(path)) return path;
		StringBuilder patternBuilder = new StringBuilder();
		int end = 0;
		int start;
		String match;
		//匹配器
		Matcher m = Pattern.compile(WILDCARD).matcher(path);
		//匹配字符串
		while (m.find()) {
			//匹配的字符子串的起始索引
			start = m.start();
			//正则标记字符子串：\Q str.sub(上次匹配子串的末尾,本次匹配子串的索引) \E
			patternBuilder.append(quote(path, end, start));
			//匹配的字符子串，等效于str.substring(m.start(), m.end())
			match = m.group();
			//ANT通配符转换为正则表达式
			if (StringConstant.QUESTION.equals(match)) {
				// 匹配单个字符：  ? == .  
				patternBuilder.append(StringConstant.POINT);
			} else if (StringConstant.DOUBLE_STAR.equals(match)) {
				// 匹配N个字符： ** == .* 
				patternBuilder.append(S1);
			} else if (StringConstant.STAR.equals(match)) {
				// 前缀匹配：* == [^/]*
				patternBuilder.append(S2);
			}
			//匹配的字符子串的末尾索引
			end = m.end();
		}
		//正则标记剩余字符子串
		patternBuilder.append(quote(path, end, path.length()));
		return patternBuilder.toString();
	}
	/**
	 * 转换正则表达式为ANT路径
	 * 
	 * @param regex 正则表达式
	 * 
	 * @return ANT路径：.==?，[^/]*==*，.*==**
	 *
	 * @author 延晓磊
	 */
	public static String toAnt(String regex){
		return toAnt(regex, false);
	}
	
	/**
	 * 转换正则表达式为ANT路径
	 * 
	 * @param regex 正则表达式，末尾包含.jpg之类的扩展名
	 * @param remainEndPoint 表示末尾的.是扩展名，而非通配符
	 * 
	 * @return ANT路径：.==? | 末尾的.保留，[^/]*==*，.*==**
	 *
	 * @author 延晓磊
	 */
	public static String toAnt(String regex, boolean remainEndPoint){
		regex = StringUtils.trimToEmpty(regex);
		//正则标记(Pattern.quote)过的字符串
		if (regex.contains(S3) && regex.contains(S4)) {
			regex = regex.replaceAll(S5, StringConstant.EMPTY);
			regex = regex.replaceAll(S6, StringConstant.EMPTY);
		}
		// .* == *
		regex = regex.replaceAll(S7, StringConstant.DOUBLE_STAR);
		// [^/]* == *
		regex = regex.replaceAll(S8, StringConstant.STAR);
		// . == ?
		regex = regex.replaceAll(S9, StringConstant.QUESTION);
		// 末尾的?改回为扩展名.
		if (remainEndPoint) {
			int i = regex.lastIndexOf(StringConstant.QUESTION);
			if (i != -1) regex = regex.substring(0, i)+StringConstant.POINT+regex.substring(i+1);
		}
		return regex;
	}
	
	/**
	 * 正则标记字符串：用\Q...\E包裹字符串，避免在Match.find时重复匹配，但可以作为正则表达式匹配
	 * 
	 * @param str 字符串
	 * @param start 子串起始
	 * @param end 子串末尾
	 * @return \Q..子串..\E
	 *
	 * @author 延晓磊
	 */
	private static String quote(String str, int start, int end) {
		//最后一个匹配的字符
		if (start == end) return StringConstant.EMPTY;
		//将字符串...转换为\Q...\E的形式，标记为正则过的字符串
		return Pattern.quote(str.substring(start, end));
	}

}
